﻿using System.Collections.Concurrent;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace BlazorAdmin.Extensions
{
    /// <summary>
    /// POCO 扩展类
    /// </summary>
    public static class PocoExtensions
    {
        private struct Key
        {
            public string TypeName { get; set; }

            public string PrimaryName { get; set; }
        }

        private static readonly ConcurrentDictionary<Key, Delegate> _cache = new();

        /// <summary>
        /// 将泛型 Poco 集合转化为主键集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="pocos"></param>
        /// <param name="primaryKeyName"></param>
        /// <returns></returns>
        public static IEnumerable<object?>? ToPrimarys<T>(this IEnumerable<T> pocos, string primaryKeyName)
        {
            string? fullName = typeof(T).FullName;
            if (string.IsNullOrEmpty(fullName))
                return Array.Empty<object>();
            Delegate invoker = _cache.GetOrAdd(new Key { TypeName = fullName, PrimaryName = primaryKeyName },
                (Key key) => GetPropertyValueLambda<T>(key.PrimaryName).Compile());
            return pocos.Select((T p) => invoker.DynamicInvoke(p));
        }

        /// <summary>
        /// 获取属性方法 Lambda 表达式
        /// </summary>
        /// <typeparam name="TModel"></typeparam>
        /// <param name="name"></param>
        /// <returns></returns>
        /// <exception cref="InvalidOperationException"></exception>
        private static Expression<Func<TModel, object>> GetPropertyValueLambda<TModel>(string name)
        {
            PropertyInfo? property = typeof(TModel).GetProperty(name);
            if (property == null)
            {
                DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new(18, 2);
                defaultInterpolatedStringHandler.AppendLiteral("类型 ");
                defaultInterpolatedStringHandler.AppendFormatted(typeof(TModel).Name);
                defaultInterpolatedStringHandler.AppendLiteral(" 未找到 ");
                defaultInterpolatedStringHandler.AppendFormatted(name);
                defaultInterpolatedStringHandler.AppendLiteral(" 属性，无法获取其值");
                throw new InvalidOperationException(defaultInterpolatedStringHandler.ToStringAndClear());
            }

            ParameterExpression parameterExpression = Expression.Parameter(typeof(TModel));
            return Expression.Lambda<Func<TModel, object>>(Expression.Convert(Expression.Property(parameterExpression, property), typeof(object)), new ParameterExpression[1]
            {
                parameterExpression
            });
        }
    }
}
